﻿using System;
using l1cs.server.model;
using l1cs.server.model.Instance;
using l1cs.server.serverpackets;
using System.Collections.Generic;
using System.Xml;
using System.Collections;
using l1cs.server.datatables;
using l1cs.server.templates;

namespace l1cs.server.model.npc.action
{
    public class L1NpcMakeItemAction : L1NpcXmlAction
    {
        private List<L1ObjectAmount<int>> _materials = new List<L1ObjectAmount<int>>();
        private List<L1ObjectAmount<int>> _items = new List<L1ObjectAmount<int>>();
        private bool _isAmountInputable;
        private L1NpcAction _actionOnSucceed;
        private L1NpcAction _actionOnFail;

        public L1NpcMakeItemAction() { }

        public L1NpcMakeItemAction( XmlReader reader )
            : base( reader )
        {
            Hashtable attribute = L1NpcXmlParser.getAllAttribute( reader );
            _isAmountInputable = L1NpcXmlParser.getBoolAttribute( (string)attribute["AmountInputable"], true );

            while ( reader.Read() )
            {
                // 開始タグを発見した場合
                if ( reader.NodeType == XmlNodeType.Element )
                {
                    if ( String.Compare( reader.Name, "Material", true ) == 0 )
                    {
                        attribute = L1NpcXmlParser.getAllAttribute( reader );
                        int id = Convert.ToInt32( attribute["ItemId"] );
                        int amount = Convert.ToInt32( attribute["Amount"] );
                        _materials.Add( new L1ObjectAmount<int>( id, amount ) );
                        continue;
                    }
                    if ( String.Compare( reader.Name, "Item", true ) == 0 )
                    {
                        attribute = L1NpcXmlParser.getAllAttribute( reader );
                        int id = Convert.ToInt32( attribute["ItemId"] );
                        int amount = Convert.ToInt32( attribute["Amount"] );
                        _items.Add( new L1ObjectAmount<int>( id, amount ) );
                        continue;
                    }
                    if ( String.Compare( reader.Name, "Succeed", true ) == 0 )
                    {
                        _actionOnSucceed = new L1NpcListedAction( reader );
                    }
                    if ( String.Compare( reader.Name, "Fail", true ) == 0 )
                    {
                        _actionOnFail = new L1NpcListedAction( reader );
                    }
                }
                else if ( reader.NodeType == XmlNodeType.EndElement )
                {
                    break;
                }
            }

            if ( _items.Count == 0 || _materials.Count == 0 )
            {
                throw new Exception();
            }
        }

        private bool makeItems(L1PcInstance pc, String npcName, int amount) {
            if (amount <= 0) {
                return false;
            }

            bool isEnoughMaterials = true;
            foreach (L1ObjectAmount<int> material in _materials) {
                if (!pc.getInventory().checkItemNotEquipped(material.getObject(),
                        material.getAmount() * amount)) {
                    L1Item temp = ItemTable.getInstance().getTemplate(
                            material.getObject());
                    pc.sendPackets(new S_ServerMessage(337, temp.getName() + "("
                            + ((material.getAmount() * amount) - pc.getInventory()
                            .countItems(temp.getItemId())) + ")")); // \f1%0が不足しています。
                    isEnoughMaterials = false;
                }
            }
            if (!isEnoughMaterials) {
                return false;
            }

            // 容量と重量の計算
            int countToCreate = 0; // アイテムの個数（纏まる物は1個）
            int weight = 0;

            foreach (L1ObjectAmount<int> makingItem in _items) {
                L1Item temp = ItemTable.getInstance().getTemplate(
                        makingItem.getObject());
                if (temp.isStackable()) {
                    if (!pc.getInventory().checkItem(makingItem.getObject())) {
                        countToCreate += 1;
                    }
                } else {
                    countToCreate += makingItem.getAmount() * amount;
                }
                weight += temp.getWeight() * (makingItem.getAmount() * amount)
                        / 1000;
            }
            // 容量確認
            if (pc.getInventory().getSize() + countToCreate > 180) {
                pc.sendPackets(new S_ServerMessage(263)); // \f1一人のキャラクターが持って歩けるアイテムは最大180個までです。
                return false;
            }
            // 重量確認
            if (pc.getMaxWeight() < pc.getInventory().getWeight() + weight) {
                pc.sendPackets(new S_ServerMessage(82)); // アイテムが重すぎて、これ以上持てません。
                return false;
            }

            foreach (L1ObjectAmount<int> material in _materials) {
                // 材料消費
                pc.getInventory().consumeItem(material.getObject(),
                        material.getAmount() * amount);
            }

            foreach (L1ObjectAmount<int> makingItem in _items) {
                L1ItemInstance item = pc.getInventory().storeItem(
                        makingItem.getObject(), makingItem.getAmount() * amount);
                if (item != null) {
                    String itemName = ItemTable.getInstance().getTemplate(
                            makingItem.getObject()).getName();
                    if (makingItem.getAmount() * amount > 1) {
                        itemName = itemName + " (" + makingItem.getAmount()
                                * amount + ")";
                    }
                    pc.sendPackets(new S_ServerMessage(143, npcName, itemName)); // \f1%0が%1をくれました。
                }
            }
            return true;
        }

        /**
         * 指定されたインベントリ内に、素材が何セットあるか数える
         */
        private int countNumOfMaterials( L1PcInventory inv )
        {
            int count = int.MaxValue;
            foreach ( L1ObjectAmount<int> material in _materials )
            {
                int numOfSet = inv.countItems( material.getObject() ) / material.getAmount();
                count = Math.Min( count, numOfSet );
            }
            return count;
        }

        override public L1NpcHtml execute( String actionName, L1PcInstance pc, L1Object obj, byte[] args )
        {
            int numOfMaterials = countNumOfMaterials( pc.getInventory() );
            if ( 1 < numOfMaterials && _isAmountInputable )
            {
                pc.sendPackets( new S_HowManyMake( obj.getId(), numOfMaterials,
                        actionName ) );
                return null;
            }
            return executeWithAmount( actionName, pc, obj, 1 );
        }

        //@Override
        public L1NpcHtml executeWithAmount( String actionName, L1PcInstance pc,
                L1Object obj, int amount )
        {
            L1NpcInstance npc = (L1NpcInstance)obj;
            L1NpcHtml result = null;
            if ( makeItems( pc, npc.getNpcTemplate().get_name(), amount ) )
            {
                if ( _actionOnSucceed != null )
                {
                    result = _actionOnSucceed.execute( actionName, pc, obj,
                            new byte[0] );
                }
            }
            else
            {
                if ( _actionOnFail != null )
                {
                    result = _actionOnFail
                            .execute( actionName, pc, obj, new byte[0] );
                }
            }
            return result == null ? L1NpcHtml.HTML_CLOSE : result;
        }
    }
}